Preparing for iOS Lifecycle, View Controllers, SwiftUI, and UIKit Questions
Course: iOS App Development
Topic: Preparing for iOS Lifecycle, View Controllers, SwiftUI, and UIKit Questions
Course Link: iOS App Development Training
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction
Understanding the iOS application lifecycle, view controller lifecycle, SwiftUI, and UIKit is essential for building reliable iPhone and iPad applications. These concepts are also frequently discussed in iOS developer interviews because they demonstrate whether a developer understands how an application launches, displays content, responds to user interactions, handles background transitions, and manages its interface.
iOS provides two major user interface development approaches: UIKit and SwiftUI. UIKit uses view controllers, views, delegates, and imperative programming patterns. SwiftUI uses a declarative approach in which developers describe the interface based on application state. Both frameworks can be used independently or integrated into the same application.
This topic explains important lifecycle methods, application states, view controller callbacks, SwiftUI lifecycle concepts, UIKit components, framework differences, practical examples, and common interview questions.
2. Learning Objectives
- Understand the iOS application lifecycle and its different states.
- Explain the responsibilities of AppDelegate and SceneDelegate.
- Understand the purpose and sequence of UIViewController lifecycle methods.
- Learn how SwiftUI manages application and view lifecycles.
- Understand the differences between UIKit and SwiftUI.
- Learn how to manage navigation, state, and user interface updates.
- Prepare answers for common iOS lifecycle and framework interview questions.
- Demonstrate lifecycle concepts through practical Swift examples.
3. Understanding the iOS Application Lifecycle
The iOS application lifecycle describes the different states an application passes through during its execution. The operating system controls these transitions and informs the application when it becomes active, inactive, enters the background, or needs to respond to other system events.
3.1 Main Application States
| State |
Description |
Typical Activity |
| Not Running |
The application has not been launched or has been terminated. |
No application code is currently executing. |
| Inactive |
The application is running but temporarily not receiving user events. |
Transitioning between active and background states or responding to interruptions. |
| Active |
The application is in the foreground and receiving user interactions. |
Handling taps, gestures, navigation, and interface updates. |
| Background |
The application is no longer visible but may receive limited execution time. |
Finishing permitted tasks and saving important application state. |
| Suspended |
The application remains in memory but does not execute code. |
Waiting until the operating system resumes or terminates it. |
3.2 Typical Application State Flow
Not Running
|
v
Application Launch
|
v
Inactive
|
v
Active
|
v
Inactive
|
v
Background
|
v
Suspended
|
v
Terminated or Resumed
Important: iOS controls application scheduling and may terminate a suspended application when system resources are required. Developers should not assume that every application will receive a final termination callback before being removed from memory.
3.3 Why Application Lifecycle Management Is Important
- Data protection: Save important information when an application moves away from the foreground.
- Resource management: Stop unnecessary processing when the application is not visible.
- User experience: Restore the interface when users return to the application.
- Performance: Avoid unnecessary background operations.
- Reliability: Handle interruptions, memory warnings, and system events correctly.
4. Understanding AppDelegate
AppDelegate is an object that manages application-level events and shared application behavior. It conforms to the UIApplicationDelegate protocol and receives callbacks associated with launching and managing the application process.
4.1 Responsibilities of AppDelegate
- Perform application launch configuration.
- Initialize shared services and application dependencies.
- Configure notification handling.
- Respond to application-level system events.
- Coordinate application-wide functionality.
4.2 Important AppDelegate Methods
| Method |
Purpose |
| application(_:didFinishLaunchingWithOptions:) |
Performs initial application setup after launch. |
| applicationWillResignActive(_:) |
Notifies the application that it is about to become inactive in an app-based lifecycle. |
| applicationDidEnterBackground(_:) |
Notifies the application that it entered the background in an app-based lifecycle. |
| applicationWillEnterForeground(_:) |
Notifies the application that it is moving toward the foreground in an app-based lifecycle. |
| applicationDidBecomeActive(_:) |
Notifies the application that it became active in an app-based lifecycle. |
| applicationWillTerminate(_:) |
Provides a termination callback in situations where the system calls it. |
4.3 AppDelegate Example
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
print("Application launched")
return true
}
func applicationDidBecomeActive(
_ application: UIApplication
) {
print("Application became active")
}
}
Explanation: The launch callback is used to perform application-level initialization. The active-state callback can be used to refresh shared information or resume appropriate application-wide operations in an app-based lifecycle.
Interview point: In scene-based applications, many foreground and background user-interface lifecycle events are handled by SceneDelegate rather than the older AppDelegate callbacks.
5. Understanding SceneDelegate
SceneDelegate manages the lifecycle of an individual application interface, known as a scene. A scene represents one instance of the application's user interface. On supported devices, an application can have multiple scenes, and each scene can have its own state.
5.1 Responsibilities of SceneDelegate
- Connect a scene to the application.
- Create and configure the scene window when required.
- Set the root view controller.
- Respond to scene activation and background transitions.
- Restore or clean up scene-specific resources.
5.2 Important SceneDelegate Methods
| Method |
Purpose |
| scene(_:willConnectTo:options:) |
Connects a scene and configures its initial interface. |
| sceneDidBecomeActive(_:) |
Notifies that the scene became active. |
| sceneWillResignActive(_:) |
Notifies that the scene is about to become inactive. |
| sceneWillEnterForeground(_:) |
Notifies that the scene is moving toward the foreground. |
| sceneDidEnterBackground(_:) |
Notifies that the scene entered the background. |
| sceneDidDisconnect(_:) |
Notifies that the scene was disconnected and its resources may need cleanup. |
5.3 SceneDelegate Example
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else {
return
}
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window
}
func sceneDidBecomeActive(_ scene: UIScene) {
print("Scene became active")
}
func sceneDidEnterBackground(_ scene: UIScene) {
print("Scene entered background")
}
}
Explanation: The scene connection method creates a window associated with the supplied UIWindowScene and assigns a root view controller. Other methods respond to the scene's state transitions.
5.4 AppDelegate vs SceneDelegate
| AppDelegate |
SceneDelegate |
| Manages application-process-level events. |
Manages an individual user-interface scene. |
| Handles application launch configuration. |
Handles scene connection and interface setup. |
| Coordinates shared application services. |
Coordinates scene-specific windows and state. |
| One application process has an application delegate. |
An application may have multiple scene delegate instances. |
| Conforms to UIApplicationDelegate. |
Typically conforms to UIWindowSceneDelegate. |
6. Understanding UIViewController
UIViewController is a fundamental UIKit class used to manage a screen or a portion of an application's interface. It controls the view hierarchy, coordinates user interactions, manages navigation, and responds to lifecycle events.
6.1 Responsibilities of a View Controller
- Manage the screen's root view and child views.
- Configure labels, buttons, images, and other interface elements.
- Handle user interactions.
- Coordinate data between the model and view.
- Manage navigation and presentation of other view controllers.
- Respond to layout and lifecycle events.
6.2 Creating a Basic UIViewController
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Home Screen"
print("View controller loaded")
}
}
Explanation: The class inherits from UIViewController. The viewDidLoad method is overridden to configure the screen after its view hierarchy has loaded.
7. UIViewController Lifecycle Methods
UIViewController provides several lifecycle methods that notify developers when a view is loaded, about to appear, becomes visible, or is removed from the screen.
7.1 Important Lifecycle Methods
| Method |
When It Is Called |
Common Use |
| loadView() |
When the controller needs its root view and no view has been provided. |
Create the view hierarchy programmatically. |
| viewDidLoad() |
After the view hierarchy has loaded into memory. |
Initial setup and one-time configuration. |
| viewWillAppear(_:) |
Just before the view appears. |
Refresh information before display. |
| viewIsAppearing(_:) |
As the view is appearing, after its traits and geometry are updated. |
Perform appearance-related configuration that needs current view information. |
| viewDidAppear(_:) |
After the view appears. |
Start animations or visible-screen tasks. |
| viewWillDisappear(_:) |
Just before the view disappears. |
Prepare to leave the screen or stop temporary activity. |
| viewDidDisappear(_:) |
After the view disappears. |
Stop screen-specific work or clean up temporary resources. |
| viewWillLayoutSubviews() |
Before the controller lays out its subviews. |
Respond to layout preparation. |
| viewDidLayoutSubviews() |
After the controller lays out its subviews. |
Perform layout-dependent adjustments. |
7.2 Typical View Controller Lifecycle Sequence
loadView()
|
v
viewDidLoad()
|
v
viewWillAppear(_:)
|
v
viewIsAppearing(_:)
|
v
viewWillLayoutSubviews()
|
v
viewDidLayoutSubviews()
|
v
viewDidAppear(_:)
|
v
viewWillDisappear(_:)
|
v
viewDidDisappear(_:)
Note: Layout callbacks can run multiple times. Appearance and disappearance callbacks can also repeat whenever a controller appears or disappears. The exact sequence depends on presentation, navigation, containment, and layout behavior.
7.3 Difference Between viewDidLoad and viewDidAppear
| viewDidLoad() |
viewDidAppear(_:) |
| Called after the view hierarchy loads. |
Called after the view becomes visible. |
| Used for initial setup. |
Used for tasks that require the screen to be visible. |
| Usually called once per loaded view instance. |
May be called multiple times. |
| Suitable for configuring controls and bindings. |
Suitable for starting visible animations or appearance-dependent work. |
7.4 Practical Lifecycle Logging Example
import UIKit
class ProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("1. View Did Load")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("2. View Will Appear")
}
override func viewIsAppearing(_ animated: Bool) {
super.viewIsAppearing(animated)
print("3. View Is Appearing")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("4. View Did Appear")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("5. View Will Disappear")
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
print("6. View Did Disappear")
}
}
Run the application, open the profile screen, navigate to another screen, and return. Observe the console output to understand when the lifecycle methods are called.
8. Understanding SwiftUI
SwiftUI is Apple's declarative user interface framework. Developers describe what the interface should display for a given state, and SwiftUI updates the interface when relevant state changes.
8.1 Key Features of SwiftUI
- Declarative syntax: Describe the desired interface instead of manually updating every visual component.
- State management: Update the interface when application data changes.
- Reusable views: Build smaller views and combine them into larger interfaces.
- Live previews: Preview supported interface changes during development.
- Cross-platform support: Build interfaces for Apple platforms using a shared framework.
- Swift integration: Use Swift language features directly in interface code.
8.2 Creating a Basic SwiftUI View
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 16) {
Text("Welcome to iOS Development")
.font(.title)
.fontWeight(.bold)
Text("Learning SwiftUI")
.foregroundStyle(.secondary)
Button("Get Started") {
print("Button tapped")
}
}
.padding()
}
}
Explanation: ContentView conforms to the View protocol. Its body property describes the interface using a vertical stack, text views, and a button.
9. SwiftUI Application Lifecycle
SwiftUI applications commonly use the App protocol as their application entry point. The @main attribute identifies the application structure that starts the app. The Scene declaration describes the interface or window content presented by the application.
9.1 Creating a SwiftUI App
import SwiftUI
@main
struct LearningApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Explanation:
@main identifies the application's entry point.
App defines the main application structure.
body declares the application's scenes.
WindowGroup creates a group of windows or scenes containing the main interface.
ContentView() is the initial interface displayed inside the window group.
9.2 Monitoring ScenePhase
SwiftUI provides the ScenePhase environment value to observe whether a scene is active, inactive, or in the background.
import SwiftUI
struct ContentView: View {
@Environment(\.scenePhase) private var scenePhase
var body: some View {
Text("Application Lifecycle Example")
.padding()
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
print("Scene is active")
case .inactive:
print("Scene is inactive")
case .background:
print("Scene entered background")
@unknown default:
print("Unknown scene phase")
}
}
}
}
Explanation: The environment property reads the current scene phase. The onChange modifier responds when the phase changes. This is useful for pausing temporary activities, saving state, and refreshing information when appropriate.
9.3 SwiftUI View Lifecycle Modifiers
| Modifier |
Purpose |
| onAppear |
Runs an action when a view appears. |
| onDisappear |
Runs an action when a view disappears. |
| task |
Starts an asynchronous task associated with a view's lifetime. |
| onChange |
Responds when an observed value changes. |
| refreshable |
Provides a pull-to-refresh action for supported views. |
9.4 onAppear and onDisappear Example
import SwiftUI
struct DashboardView: View {
var body: some View {
VStack {
Text("Dashboard")
.font(.largeTitle)
}
.onAppear {
print("Dashboard appeared")
loadDashboardData()
}
.onDisappear {
print("Dashboard disappeared")
}
}
private func loadDashboardData() {
print("Loading dashboard information")
}
}
Important: onAppear is not guaranteed to run only once. A view may appear again due to navigation, conditional rendering, or other interface changes. Avoid treating it as a one-time application initialization method.
9.5 Using task for Asynchronous Work
import SwiftUI
struct UserListView: View {
@State private var users: [String] = []
var body: some View {
List(users, id: \.self) { user in
Text(user)
}
.task {
await loadUsers()
}
}
private func loadUsers() async {
try? await Task.sleep(for: .seconds(1))
users = ["Aarav", "Priya", "Rahul"]
}
}
The task modifier is useful for asynchronous operations such as loading data. SwiftUI manages the task in relation to the view's lifecycle and can cancel it when the view disappears. Developers should still design asynchronous operations to handle cancellation correctly.
10. SwiftUI State Management Concepts
SwiftUI uses property wrappers and observable data models to manage information that affects the interface. When relevant state changes, SwiftUI recalculates the affected view body and updates the displayed interface.
| Property Wrapper |
Purpose |
Typical Usage |
| @State |
Stores local view-owned state. |
Form input, selected tab, counter. |
| @Binding |
Provides read-write access to state owned elsewhere. |
Child view editing parent state. |
| @StateObject |
Creates and owns an ObservableObject reference in a view. |
Legacy ObservableObject-based view models. |
| @ObservedObject |
Observes an externally owned ObservableObject. |
Receiving an existing view model. |
| @EnvironmentObject |
Reads an ObservableObject supplied through the environment. |
Shared application data. |
| @Environment |
Reads values supplied through the environment. |
Scene phase, color scheme, dismiss action. |
| @Observable |
Enables observation for a model using the Observation framework. |
Modern observable data models. |
10.1 @State Example
import SwiftUI
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 15) {
Text("Count: \(count)")
.font(.title)
Button("Increase") {
count += 1
}
}
.padding()
}
}
When the button is tapped, the count value changes. SwiftUI detects the state update and refreshes the displayed text.
10.2 @Binding Example
import SwiftUI
struct ParentView: View {
@State private var username = ""
var body: some View {
VStack {
Text("Username: \(username)")
UsernameEditor(username: $username)
}
.padding()
}
}
struct UsernameEditor: View {
@Binding var username: String
var body: some View {
TextField("Enter username", text: $username)
.textFieldStyle(.roundedBorder)
}
}
The parent owns the username state, while the child receives a binding that allows it to read and update the same value.
11. Understanding UIKit
UIKit is Apple's framework for developing application interfaces on iOS and related platforms. It provides components for views, view controllers, navigation, gestures, animations, tables, collections, and user interaction.
11.1 Important UIKit Components
| Component |
Purpose |
| UIView |
Represents a rectangular area used to display content and handle interactions. |
| UIViewController |
Manages a screen or portion of an interface. |
| UILabel |
Displays text. |
| UIButton |
Provides a tappable button. |
| UITextField |
Accepts single-line text input. |
| UITextView |
Displays or accepts multi-line text. |
| UITableView |
Displays information in a vertically arranged list. |
| UICollectionView |
Displays flexible collections of items. |
| UINavigationController |
Manages a stack-based navigation flow. |
| UITabBarController |
Manages multiple sections accessible through a tab bar. |
| UIScrollView |
Supports scrolling content. |
11.2 Creating a Button in UIKit
import UIKit
class HomeViewController: UIViewController {
private let actionButton = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
actionButton.setTitle("Continue", for: .normal)
actionButton.addTarget(
self,
action: #selector(buttonTapped),
for: .touchUpInside
)
actionButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(actionButton)
NSLayoutConstraint.activate([
actionButton.centerXAnchor.constraint(
equalTo: view.centerXAnchor
),
actionButton.centerYAnchor.constraint(
equalTo: view.centerYAnchor
)
])
}
@objc private func buttonTapped() {
print("Continue button tapped")
}
}
This example creates a button programmatically, adds it to the view hierarchy, positions it using Auto Layout, and connects a target-action method to its tap event.
12. SwiftUI vs UIKit
Both SwiftUI and UIKit are used to develop iOS interfaces, but they follow different programming approaches.
| Feature |
SwiftUI |
UIKit |
| Programming style |
Declarative |
Primarily imperative |
| Interface definition |
Swift code describing the desired interface |
Views configured through code, storyboards, or other interface tools |
| Lifecycle |
App, Scene, and View lifecycle APIs |
App/Scene delegates and UIViewController lifecycle |
| State updates |
State-driven interface updates |
Developers commonly update views explicitly |
| Navigation |
NavigationStack and related APIs |
UINavigationController and presentation APIs |
| Layout |
Stacks, grids, modifiers, and layout protocols |
Auto Layout, constraints, frames, and view layout APIs |
| Reuse |
Custom reusable View structures |
Reusable UIView and UIViewController subclasses |
| Integration |
Can host UIKit components |
Can host SwiftUI views |
12.1 When Should You Use SwiftUI?
- When building a new application with a state-driven interface.
- When creating reusable interface components.
- When working with declarative layouts and previews.
- When developing applications across multiple Apple platforms.
- When the required interface and platform APIs are well supported by SwiftUI.
12.2 When Should You Use UIKit?
- When maintaining an existing UIKit application.
- When a project depends on UIKit-specific components or APIs.
- When integrating with established UIKit-based libraries.
- When detailed control over view controller behavior is required.
- When building complex interfaces using mature UIKit capabilities.
13. Integrating SwiftUI and UIKit
SwiftUI and UIKit can work together in a single application. This is useful when gradually modernizing an existing application or when a project requires a component that is easier to implement using the other framework.
13.1 Displaying SwiftUI Inside UIKit
UIHostingController allows a SwiftUI view to be presented inside a UIKit application.
import SwiftUI
import UIKit
struct WelcomeView: View {
var body: some View {
VStack {
Text("Welcome")
.font(.largeTitle)
Text("This is a SwiftUI screen")
}
.padding()
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swiftUIView = WelcomeView()
let hostingController = UIHostingController(
rootView: swiftUIView
)
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingController.view.topAnchor.constraint(
equalTo: view.topAnchor
),
hostingController.view.bottomAnchor.constraint(
equalTo: view.bottomAnchor
),
hostingController.view.leadingAnchor.constraint(
equalTo: view.leadingAnchor
),
hostingController.view.trailingAnchor.constraint(
equalTo: view.trailingAnchor
)
])
hostingController.didMove(toParent: self)
}
}
13.2 Displaying UIKit Inside SwiftUI
UIViewRepresentable allows a UIKit UIView to be used as part of a SwiftUI interface.
import SwiftUI
import UIKit
struct CustomUIKitView: UIViewRepresentable {
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.text = "UIKit inside SwiftUI"
label.textAlignment = .center
label.font = .systemFont(ofSize: 20)
return label
}
func updateUIView(
_ uiView: UILabel,
context: Context
) {
uiView.text = "Updated UIKit Component"
}
}
struct ContentView: View {
var body: some View {
CustomUIKitView()
.frame(height: 60)
.padding()
}
}
Explanation: makeUIView creates the UIKit view, while updateUIView updates it when SwiftUI needs to synchronize its state with the represented view.
14. Navigation in UIKit and SwiftUI
14.1 UIKit Navigation Controller
UINavigationController manages a stack of view controllers. A new screen is pushed onto the stack, and the previous screen can be restored by popping the current controller.
let detailVC = DetailViewController()
navigationController?.pushViewController(
detailVC,
animated: true
)
// Return to the previous screen
navigationController?.popViewController(animated: true)
14.2 SwiftUI NavigationStack
import SwiftUI
struct NavigationExample: View {
var body: some View {
NavigationStack {
VStack(spacing: 20) {
Text("Home Screen")
NavigationLink(
"Open Details",
destination: DetailScreen()
)
}
.navigationTitle("Home")
}
}
}
struct DetailScreen: View {
var body: some View {
Text("Detail Screen")
.navigationTitle("Details")
}
}
NavigationStack manages a navigation hierarchy in SwiftUI. NavigationLink provides a declarative way to navigate between views.
15. Memory Management and Lifecycle Best Practices
Lifecycle management is closely connected to memory usage and application performance. Developers should release unnecessary resources and avoid retaining objects longer than required.
15.1 Important Best Practices
- Keep viewDidLoad focused on initial view configuration.
- Refresh screen-specific information at appropriate appearance events.
- Stop timers and observers when they are no longer required.
- Cancel unnecessary asynchronous tasks.
- Save important data before entering the background.
- Use weak references in closures when necessary to prevent retain cycles.
- Avoid performing expensive operations repeatedly during view updates.
- Respond appropriately to memory warnings.
- Do not rely on applicationWillTerminate as the only opportunity to save data.
- Test lifecycle behavior on real devices as well as simulators.
15.2 Avoiding Retain Cycles
class ProfileViewController: UIViewController {
var onUpdate: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onUpdate = { [weak self] in
guard let self = self else { return }
self.refreshProfile()
}
}
private func refreshProfile() {
print("Profile refreshed")
}
}
Using a weak capture prevents the closure from strongly retaining the view controller. This is useful when a closure is stored by an object that may outlive the controller.
16. Common Lifecycle and Framework Mistakes
| Mistake |
Why It Is a Problem |
Recommended Solution |
| Loading data repeatedly in viewDidAppear. |
The method may run each time the screen appears. |
Choose the correct lifecycle event and cache data when appropriate. |
| Saving data only during termination. |
The application may be terminated without receiving the expected callback. |
Save important changes during normal operation and background transitions. |
| Updating UIKit views from a background thread. |
Can cause inconsistent interface behavior. |
Perform UI updates on the main thread or MainActor. |
| Assuming onAppear runs only once. |
SwiftUI views may appear multiple times. |
Make appearance actions safe to repeat. |
| Creating unnecessary state properties. |
Can make data ownership and updates difficult to understand. |
Use the appropriate state ownership mechanism. |
| Ignoring task cancellation. |
Unnecessary work may continue after a view disappears. |
Use cancellation-aware asynchronous code. |
| Confusing AppDelegate with SceneDelegate. |
Application-wide and scene-specific events have different responsibilities. |
Separate process-level and scene-level logic. |
| Forgetting child controller containment. |
Embedded controllers may not receive correct lifecycle and layout behavior. |
Use addChild, didMove, and the appropriate removal methods. |
17. Practical Project: Lifecycle Demonstration Application
Build a small iOS application that demonstrates application state changes, view controller lifecycle methods, and SwiftUI appearance events.
17.1 Project Requirements
- Create a home screen with a welcome message.
- Display a button that navigates to a profile screen.
- Print UIViewController lifecycle events in the Xcode console.
- Display the current scene phase in a SwiftUI screen.
- Use onAppear and onDisappear to observe view visibility.
- Save a small piece of user data before the application enters the background.
- Test returning to the application after switching to another app.
17.2 Example SwiftUI Lifecycle Screen
import SwiftUI
struct LifecycleDemoView: View {
@Environment(\.scenePhase) private var scenePhase
@State private var message = "Application is ready"
var body: some View {
VStack(spacing: 20) {
Text("iOS Lifecycle Demo")
.font(.title)
Text(message)
.multilineTextAlignment(.center)
Button("Update Status") {
message = "User interacted with the app"
}
}
.padding()
.onAppear {
print("View appeared")
}
.onDisappear {
print("View disappeared")
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
message = "Application is active"
case .inactive:
message = "Application is inactive"
case .background:
message = "Application is in background"
@unknown default:
message = "Unknown state"
}
}
}
}
17.3 Expected Learning Outcome
After completing this project, learners should be able to identify lifecycle callbacks, understand how views appear and disappear, observe scene transitions, and explain the differences between UIKit and SwiftUI approaches.
18. iOS Lifecycle Interview Questions and Answers
Q1. What is the iOS application lifecycle?
Answer: The iOS application lifecycle describes the different execution states an application passes through, including not running, inactive, active, background, and suspended. Developers use lifecycle events to manage resources, save information, and prepare the interface for user interaction.
Q2. What is AppDelegate?
Answer: AppDelegate is an object that conforms to UIApplicationDelegate and handles application-level events such as launch configuration and shared application behavior.
Q3. What is SceneDelegate?
Answer: SceneDelegate manages the lifecycle of an individual user-interface scene. It handles scene connection, activation, background transitions, and scene-specific window configuration.
Q4. What is the difference between AppDelegate and SceneDelegate?
Answer: AppDelegate manages application-process-level behavior, while SceneDelegate manages the lifecycle and interface configuration of an individual scene.
Q5. What is UIViewController?
Answer: UIViewController is a UIKit class responsible for managing a screen or a portion of an interface, including its view hierarchy, user interactions, navigation, and lifecycle events.
Q6. What is viewDidLoad?
Answer: viewDidLoad is called after the view controller's view hierarchy has loaded into memory. It is commonly used for initial configuration, setting up controls, and registering required resources.
Q7. What is the difference between viewWillAppear and viewDidAppear?
Answer: viewWillAppear is called just before the view appears, whereas viewDidAppear is called after the view has appeared. The first is useful for preparing the interface, while the second is useful for tasks that require the view to be visible.
Q8. What is SwiftUI?
Answer: SwiftUI is Apple's declarative user interface framework. Developers describe the interface using Swift views, and the framework updates the interface when relevant state changes.
Q9. What is the App protocol in SwiftUI?
Answer: The App protocol defines the structure and entry point of a SwiftUI application. A type conforming to App provides a body property that declares the application's scenes.
Q10. What is ScenePhase?
Answer: ScenePhase is an environment value that represents the current state of a SwiftUI scene. Its common values are active, inactive, and background.
Q11. What is the difference between onAppear and viewDidLoad?
Answer: onAppear is a SwiftUI view lifecycle modifier that runs when a view appears. viewDidLoad is a UIKit view controller callback that runs after the controller's view hierarchy loads. They serve different lifecycle models and are not exact equivalents.
Q12. What is @State in SwiftUI?
Answer: @State stores local, view-owned mutable state. When the value changes, SwiftUI updates the parts of the interface that depend on it.
Q13. What is @Binding?
Answer: @Binding provides read-write access to a value owned by another view or state holder. It allows child views to modify data without owning that data.
Q14. What is the difference between SwiftUI and UIKit?
Answer: SwiftUI uses a declarative, state-driven approach, while UIKit primarily uses an imperative approach with views and view controllers. Both frameworks can be combined in the same application.
Q15. What is UIHostingController?
Answer: UIHostingController is a UIKit view controller that hosts a SwiftUI view. It allows developers to integrate SwiftUI interfaces into UIKit applications.
Q16. What is UIViewRepresentable?
Answer: UIViewRepresentable is a SwiftUI protocol that allows developers to create and manage UIKit UIView components inside SwiftUI interfaces.
Q17. What happens when an iOS application enters the background?
Answer: The application or scene becomes non-visible and may receive limited time to complete important tasks. Developers should save important state, stop unnecessary work, and release resources where possible.
Q18. Is applicationWillTerminate always called?
Answer: No. iOS may suspend and terminate an application without calling applicationWillTerminate. Important information should be saved before termination becomes necessary.
Q19. What is NavigationStack?
Answer: NavigationStack is a SwiftUI container that manages a navigation hierarchy and supports navigation between views.
Q20. What is UINavigationController?
Answer: UINavigationController is a UIKit container view controller that manages a stack of view controllers and supports push and pop navigation.
Q21. Why should UI updates happen on the main thread?
Answer: UIKit interface operations are expected to occur on the main thread. Performing UI updates from background threads can cause inconsistent behavior and runtime problems. MainActor can be used to isolate UI-related work.
Q22. What is the purpose of viewWillDisappear?
Answer: viewWillDisappear is called before a view controller's view disappears. It can be used to prepare for navigation, stop temporary activities, or update screen-related state.
Q23. Can UIKit and SwiftUI be used together?
Answer: Yes. UIHostingController embeds SwiftUI views inside UIKit, while UIViewRepresentable and UIViewControllerRepresentable allow UIKit components to be used inside SwiftUI.
Q24. What is a retain cycle?
Answer: A retain cycle occurs when objects hold strong references to each other, preventing them from being deallocated. Weak or unowned references can help prevent retain cycles when used appropriately.
Q25. What is the purpose of viewDidLayoutSubviews?
Answer: viewDidLayoutSubviews is called after the controller's view lays out its subviews. It is useful when code needs the updated layout geometry, but it should not be used for expensive repeated work.
19. Practical Interview Preparation Checklist
- Explain all major iOS application states.
- Describe the responsibilities of AppDelegate and SceneDelegate.
- Write a basic AppDelegate and SceneDelegate implementation.
- Explain the UIViewController lifecycle sequence.
- Differentiate viewDidLoad, viewWillAppear, and viewDidAppear.
- Explain the SwiftUI App protocol and WindowGroup.
- Demonstrate ScenePhase with a SwiftUI example.
- Explain onAppear, onDisappear, and task.
- Understand @State, @Binding, and observable data models.
- Compare SwiftUI and UIKit with practical examples.
- Explain UIHostingController and UIViewRepresentable.
- Demonstrate navigation using NavigationStack and UINavigationController.
- Explain background handling, data persistence, and memory management.
- Practice explaining code examples without relying only on memorized definitions.
20. Practice Exercises
- Create a UIKit application that prints all major UIViewController lifecycle callbacks.
- Build a SwiftUI screen that displays the current ScenePhase.
- Create a counter using @State and explain how the interface updates.
- Build a parent-child interface using @Binding.
- Implement a navigation flow using UINavigationController.
- Implement the same navigation flow using NavigationStack.
- Embed a SwiftUI view inside a UIKit application using UIHostingController.
- Display a UIKit label inside SwiftUI using UIViewRepresentable.
- Test how your application behaves when it enters the background and returns to the foreground.
- Explain the difference between application lifecycle events and view controller lifecycle events.
21. Quick Revision Notes
| Concept |
Remember |
| App lifecycle |
Describes application execution states. |
| AppDelegate |
Manages application-level events. |
| SceneDelegate |
Manages individual UI scene events. |
| UIViewController |
Manages a UIKit screen or interface section. |
| viewDidLoad |
Initial view hierarchy setup. |
| viewWillAppear |
Called before the view appears. |
| viewDidAppear |
Called after the view appears. |
| SwiftUI |
Declarative user interface framework. |
| App protocol |
Defines the SwiftUI application structure. |
| ScenePhase |
Represents the current scene state. |
| @State |
Stores local view-owned state. |
| @Binding |
Shares read-write access to state. |
| UIKit |
Framework for building iOS interfaces. |
| UIHostingController |
Hosts SwiftUI content in UIKit. |
| UIViewRepresentable |
Wraps a UIKit view for SwiftUI. |
| NavigationStack |
Manages SwiftUI navigation. |
| UINavigationController |
Manages UIKit navigation stacks. |
22. Important Documentation and Learning Resources
23. Conclusion
Understanding the iOS application lifecycle, view controller lifecycle, SwiftUI, and UIKit is essential for becoming a capable iOS developer. Application lifecycle concepts explain how an app responds to system events, while view controller lifecycle methods explain how individual UIKit screens are loaded, displayed, and removed.
SwiftUI provides a declarative and state-driven approach to interface development, whereas UIKit offers a mature framework with detailed control over views and view controllers. Knowing how to use both frameworks and integrate them when required is valuable for building modern iOS applications and answering technical interview questions confidently.
To explore structured iOS App Development training, visit JustAcademy iOS App Development Training, register for a course demo, or explore the JustAcademy website.